package com.link4a.doms.exception;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import com.link4a.doms.web.JsonResult;

public class GlobalExceptionResolver implements HandlerExceptionResolver {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
	@Autowired
	private MappingJackson2HttpMessageConverter jasksonMessageConverter;
	@Autowired
	private StringHttpMessageConverter stringMessageConverter;
	
	private String errorPage;

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		List<MediaType> mediaTypes = null;
		try {
			mediaTypes = contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
			
			boolean databaseException = ex instanceof DataAccessException;
			boolean illegalPrivilege = ex instanceof IllegalPrivilegeException;
			
			StringWriter stringWriter = new StringWriter();
			PrintWriter pringWriter = new PrintWriter(stringWriter);
			ex.printStackTrace(pringWriter);
			String stackTrace = stringWriter.getBuffer().toString();
			String message = ex.getMessage();
			
			if (databaseException) {
				message = "Database is out of service currently, please retry again latter.";
				stackTrace = null;
			}
			
			logger.error(message, ex);
			
			for (MediaType acceptMediaType : mediaTypes) {
				if (MediaType.TEXT_HTML.isCompatibleWith(acceptMediaType) || MediaType.APPLICATION_XHTML_XML.isCompatibleWith(acceptMediaType)) {
					
					if (errorPage == null) {
						try {
							if (illegalPrivilege) {
								response.sendError(HttpServletResponse.SC_FORBIDDEN, "Exception: " + message);
							} else {
								response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Exception: " + message);
							}
						} catch (IOException e) {
							logger.error("Failed to write HTML response", e);
						}
					} else {
						
						request.setAttribute("message", message);
						request.setAttribute("stackTrace", stackTrace);
						try {
							request.getRequestDispatcher(errorPage).forward(request, response);
						} catch (ServletException | IOException e) {
							logger.error("Failed to forward request to errorPage", e);
						}
					}
					
					return new ModelAndView();
					
				} else if(MediaType.APPLICATION_JSON.isCompatibleWith(acceptMediaType)) {
					JsonResult jsonResult = new JsonResult(illegalPrivilege ? JsonResult.PERMISSION_DENIED : JsonResult.ERROR);
					jsonResult.setMessage(message);
					try {
						jasksonMessageConverter.write(jsonResult, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
					} catch (IOException e) {
						logger.error("Failed to write JSON response", e);
					}
					
					return new ModelAndView();
				} else if (MediaType.TEXT_PLAIN.isCompatibleWith(acceptMediaType) || MediaType.ALL.isCompatibleWith(acceptMediaType)){
					
					try {
						stringMessageConverter.write(message, MediaType.TEXT_PLAIN, new ServletServerHttpResponse(response));
					} catch (IOException e) {
						logger.error("Failed to write TEXT response", e);
					}
					return new ModelAndView();
				}
			}
		} catch (HttpMediaTypeNotAcceptableException e1) {
			logger.error("Bad HTTP request, unrecognize media type", e1);
		}
		
		return new ModelAndView();
	}

	public void setErrorPage(String errorPage) {
		this.errorPage = errorPage;
	}
}
